/**
 * 
 */
package ai

import static org.junit.Assert.*

import org.junit.Before
import org.junit.Test

import data.Player
import data.Position
import data.Data.PlayerState
import data.Data.TeamID
import engine.Pitch

/**
 * AI engine test.
 * @author Tomasz Dąbrowski
 *
 */
class AIEngineTest {

	GameEngineFake gameEngine
	AIEngine aiEngine

	def printPitch = { Closure c ->
		(0..<Pitch.PITCH_HEIGHT).each { int y ->
			(0..<Pitch.PITCH_WIDTH).each { int x ->
				System.out.println("\t" + c(new Position(x, y)))
			}
			System.out.println()
		}
	}

	/**
	 *
	 * @throws java.lang.Exception
	 *
	 * @author Tomasz Dąbrowski
	 */
	@Before
	public void setUp() throws Exception {
		gameEngine = new GameEngineFake()
		aiEngine = new AIEngine(gameEngine.teamA(), gameEngine)
	}

	/**
	 * Test method for {@link ai.AIEngine#pitchRange()}.
	 */
	@Test
	public void testPitchRange() {
		int size = aiEngine.pitchRange().unique().size()
		assertEquals(Pitch.PITCH_WIDTH * Pitch.PITCH_HEIGHT, size)
	}

	/**
	 * Test method for {@link ai.AIEngine#posRange(data.Position)}.
	 */
	@Test
	public void testPosRange() {
		List ps = [
			[1, 1],
			[1, 2],
			[1, 3],
			[2, 1],
			[2, 3],
			[3, 1],
			[3, 2],
			[3, 3]
		].collect { new Position(it[0], it[1]) }
		Position center = new Position(2,2)
		List range = aiEngine.posRange(center, 1, false)

		assertEquals(8, range.size())
		assertTrue(range.every { Position p -> p in ps } )

		ps.add(center)
		range = aiEngine.posRange(center, 1, true)

		assertEquals(9, range.size())
		assertTrue(range.every { it in ps } )

		center = new Position(5, 5)
		range = aiEngine.posRange(center, 2, false)
		assertEquals(24, range.size())

		center = new Position(1, 1)
		range = aiEngine.posRange(center, 2, false)
		assertEquals(15, range.size())

		center = new Position(Pitch.PITCH_WIDTH-1, Pitch.PITCH_HEIGHT-1)
		range = aiEngine.posRange(center, 2, true)
		assertEquals(9, range.size())
	}

	/**
	 * Test method for {@link ai.AIEngine#posCircle(data.Position, int)}.
	 */
	@Test
	public void testPosCircle() {
		List ps = (0..7).collect {
			new Position(it, 7)
		} + (0..7).collect {
			new Position(7, it)
		}
		ps.unique()
		List range = aiEngine.posCircle(new Position(2, 2), 5)

		assertEquals(ps.size(), range.size())
		assertTrue(range.every { it in ps } )
	}

	/**
	 * Test method for {@link ai.AIEngine#pitchTeam(data.Data.TeamID)}.
	 */
	@Test
	public void testPitchTeam() {
		int size = (Pitch.PITCH_HEIGHT * Pitch.PITCH_WIDTH) / 2
		assert size > 0

		List range = aiEngine.pitchTeam(TeamID.TEAM_A).unique()
		assertEquals(size, range.size())
		assertTrue(range.every { Position p -> p.x() < Pitch.PITCH_WIDTH / 2 })

		range = aiEngine.pitchTeam(TeamID.TEAM_B).unique()
		assertEquals(size, range.size())
		assertTrue(range.every { Position p -> p.x() >= Pitch.PITCH_WIDTH / 2 })
	}

	/**
	 * Test method for {@link ai.AIEngine#getPlayerWithBall()}.
	 */
	@Test
	public void testGetPlayerWithBall() {
		Player p = gameEngine.teamB().players()[4]
		p.setHasBall(true)

		Player pWithBall = aiEngine.getPlayerWithBall()
	}

	/**
	 * Test method for {@link ai.AIEngine#getBallPosition()}.
	 */
	@Test
	public void testGetBallPosition() {
		Pitch pitch = new Pitch(gameEngine.teamA(), gameEngine.teamB())
		gameEngine.playingField = pitch

		Position pos = new Position(6, 10)
		pitch.placeBall(pos)
		Position ballPos = aiEngine.getBallPosition()
		assertEquals(pos, ballPos)

		pos = new Position(2, 7)
		pitch = new Pitch(gameEngine.teamA(), gameEngine.teamB())
		gameEngine.playingField = pitch
		Player p = gameEngine.teamA().players()[4]
		p.setHasBall(true)
		pitch.placePlayer(gameEngine.teamA().id, p, pos)
		ballPos = aiEngine.getBallPosition()
		assertEquals(pos, ballPos)
	}



	/**
	 * Test method for {@link ai.AIEngine#isFromTeam(data.Player, data.Data.TeamID)}.
	 */
	@Test
	public void testIsFromTeam() {
		Player p = gameEngine.teamA().players()[1]
		assertTrue(aiEngine.isFromTeam(p, TeamID.TEAM_A))
		assertFalse(aiEngine.isFromTeam(p, TeamID.TEAM_B))
	}

	/**
	 * Test method for {@link ai.AIEngine#calculateDPValue(data.Position, data.Data.TeamID, java.util.List)}.
	 */
	@Test
	public void testCalculateDPValue() {
		Pitch pitch = new Pitch(gameEngine.teamA(), gameEngine.teamB())
		gameEngine.playingField = pitch

		List poss = (1..11).collect {
			new Position(it, it)
		}
		gameEngine.teamB().players().eachWithIndex { Player p, int index ->
			p.setState(PlayerState.IN_PLAY)
			pitch.placePlayer(TeamID.TEAM_B, p, poss[index])
		}

		assertEquals(AIEngine.NEAR_ENEMY_DANGER_VALUE, aiEngine.calculateDPValue(new Position(0, 0), TeamID.TEAM_A))
		assertEquals(AIEngine.NEAR_ENEMY_DANGER_VALUE, aiEngine.calculateDPValue(new Position(1, 2), TeamID.TEAM_A))
		assertEquals(AIEngine.NEAR_ENEMY_DANGER_VALUE, aiEngine.calculateDPValue(new Position(2, 3), TeamID.TEAM_A))
		assertEquals(AIEngine.NEAR_ENEMY_DANGER_VALUE, aiEngine.calculateDPValue(new Position(1, 0), TeamID.TEAM_A))

		gameEngine.teamB().players().each { it.setState(PlayerState.IN_DUGOUT) }
		gameEngine.teamB().players()[0].setState(PlayerState.IN_PLAY)

		assertEquals(AIEngine.MAX_DANGER, aiEngine.calculateDPValue(new Position(3, 1), TeamID.TEAM_A))
		assertEquals((int) AIEngine.MAX_DANGER / 2, aiEngine.calculateDPValue(new Position(4, 1), TeamID.TEAM_A))
		assertEquals((int) AIEngine.MAX_DANGER / 3, aiEngine.calculateDPValue(new Position(5, 1), TeamID.TEAM_A))
	}

	/**
	 * Test method for {@link ai.AIEngine#getDangerValue(data.Position)}.
	 */
	@Test
	public void testGetDangerValue() {
		Pitch pitch = new Pitch(gameEngine.teamA(), gameEngine.teamB())
		gameEngine.playingField = pitch

		List poss = (1..11).collect {
			new Position(it, it)
		}
		gameEngine.teamB().players()[0..1].eachWithIndex { Player p, int index ->
			p.setState(PlayerState.IN_PLAY)
			pitch.placePlayer(TeamID.TEAM_B, p, poss[index])
		}
		aiEngine.updateDangerValues()

		assertEquals(AIEngine.NEAR_ENEMY_DANGER_VALUE, aiEngine.getDangerValue(new Position(0, 0)))
		assertEquals(AIEngine.NEAR_ENEMY_DANGER_VALUE, aiEngine.getDangerValue(new Position(1, 2)))
	}

	/**
	 * Test method for {@link ai.AIEngine#evaluateKickOffPosition(data.Position)}.
	 */
	@Test
	public void testEvaluateKickOffPosition() {
		Pitch pitch = new Pitch(gameEngine.teamA(), gameEngine.teamB())
		gameEngine.playingField = pitch

		Position pos = new Position(19, 8)
		Player p = gameEngine.teamB().players()[0]


		assertEquals(-3 * 7, aiEngine.evaluateKickOffPosition(pos))

		pos = new Position(19, 7)
		pitch.placePlayer(TeamID.TEAM_B, p, pos)
		pos = new Position(18, 7)
		assertEquals(-2, aiEngine.evaluateKickOffPosition(pos))
	}

	/**
	 * Test method for {@link ai.AIEngine#getKickOffBallPosition()}.
	 */
	@Test
	public void testGetKickOffBallPosition() {
		Pitch pitch = new Pitch(gameEngine.teamA(), gameEngine.teamB())
		gameEngine.playingField = pitch

		Position pos = new Position(19, 7)
		Player p = gameEngine.teamB().players()[0]
		pitch.placePlayer(TeamID.TEAM_B, p, pos)

		pos = aiEngine.getKickOffBallPosition()
		assertEquals(0, aiEngine.evaluateKickOffPosition(pos))
	}


	/**
	 * Test method for {@link ai.AIEngine#getSafestDestinationByPath(java.util.List, java.util.List)}.
	 */
	@Test
	public void testGetSafestDestinationByPath() {
		Pitch pitch = new Pitch(gameEngine.teamA(), gameEngine.teamB())
		gameEngine.playingField = pitch

		Position pos = new Position(19, 0)
		Player p = gameEngine.teamB().players()[0]
		p.setState(PlayerState.IN_PLAY)
		pitch.placePlayer(TeamID.TEAM_B, p, pos)
		aiEngine.updateDangerValues()
		assertEquals(new Position(25, 6), aiEngine.getSafestDestinationByPath([new Position(0,0)], (0..<Pitch.PITCH_HEIGHT).collect {new Position(Pitch.PITCH_WIDTH - 1, it) })[1])

		pos = new Position(19, 14)
		p = gameEngine.teamB().players()[1]
		pitch.placePlayer(TeamID.TEAM_B, p, pos)
		p.setState(PlayerState.IN_PLAY)
		aiEngine.updateDangerValues()

		assertEquals(new Position(25, 6), aiEngine.getSafestDestinationByPath([new Position(0,7)], (0..<Pitch.PITCH_HEIGHT).collect {new Position(Pitch.PITCH_WIDTH - 1, it) })[1])
	}
}